from PySide6.QtCore import QObject, QTimer, Slot, Signal, Property, QEnum, Qt
from PySide6.QtQml import QmlElement, QmlSingleton
import logging
import threading
import re
import json as JSON
import random
import struct
from .constants import *
from paramiko import SSHClient, AutoAddPolicy, RSAKey
import select

class SshWorker(QObject):
    
    connectedStateChanged = Signal()
    
    updateInstallFinished = Signal(bool, str)
    getInstallVersionFinished = Signal(bool, str)
    getBlePairingCodeFinished = Signal(bool, int)
    setBlePairingCodeFinished = Signal(bool, str)
    # killRosFinished = Signal(bool, str)
    setSerialNumberFinished = Signal(bool, int)
    getSerialNumberFinished = Signal(bool, int)
    
    # 执行命令完成信号
    execCommandFinished = Signal(bool, str)
    getBackedEnvTypeFinished = Signal(bool, str)
    setBackedEnvTypeFinished = Signal(bool, str)
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self._sshClient = SSHClient()
        self._sshClient.set_missing_host_key_policy(AutoAddPolicy())
        self._connected = False
        # self.timer = QTimer()
        
        # self.timer.timeout.connect(self._check_connection)
        # self.timer.start(500) # 每500毫秒检查一次连接状态  
        
    def isConnected(self):
        return self._connected
    
    @Slot()
    def _check_connection(self):
        if self._connected == False:
            return
        
        logging.debug("Checking SSH connection status...")
        transport = self._sshClient.get_transport()
        if not transport or not transport.is_active():
            self._connected = False
            self.connectedStateChanged.emit()
            logging.debug("SSH connection lost")
            
            
    
    @Slot(str, int, str, str, str, result=bool)
    def connectToHost(self, host, port, username, password):
        logging.debug(f"Connecting to SSH host {host}:{port} with username {username}")
        try:
            self._sshClient.connect(
                host, 
                port, 
                username, 
                password, 
                timeout=5,          # TCP连接超时5秒
                auth_timeout=5,     # 认证超时5秒
            )
            self._connected = True
        
            return True
        except Exception as e:
            logging.error(f"Failed to connect to {host}:{port}: {e}")
            return False
        finally:
            self.connectedStateChanged.emit()

    @Slot()
    def disconnectFromHost(self):
        try:
            logging.debug("Disconnecting from SSH client")
            self._sshClient.close()
            self._connected = False
            logging.debug("SSH client disconnected successfully")
            return True
        except Exception as e:
            logging.error(f"Failed to disconnect from SSH client: {e}")
            return False
        finally:
            self.connectedStateChanged.emit()
            
    @Slot(str)
    def execCommand(self, command):
        try:
            ret, output = self._execCommand(command)
            self.execCommandFinished.emit(ret, output)
        except Exception as e:
            logging.error(f"Failed to execute command {command}: {e}")
            self.execCommandFinished.emit(False, str(e))
            
    
    @Slot(str)
    def update_install(self, file_path: str):
        try:
            ret = self._pushFile(file_path, USERDATA_PATH + "/update.tar.gz")
            if not ret:
                raise Exception(f"Failed to push update package to remote host")
            ret, output = self._execCommand(f"rm -rf {REMOTE_INSTALL_PATH} && tar -xzf {USERDATA_PATH}/update.tar.gz && rm {USERDATA_PATH}/update.tar.gz && sync")
            if not ret:
                raise Exception(f"Failed to extract update package: {output}")
            self.updateInstallFinished.emit(True, "Update successful")
            logging.info("Update install successful")
        except Exception as e:
            self.updateInstallFinished.emit(False, str(e))
            logging.error(f"Update install failed: {e}")
    
    # 获取后台信息，demo,dev,pruduction
    @Slot()
    def getBackedEnv(self):
        try:
            ret, output = self._execCommand(f'cat {REMOTE_INSTALL_PATH}/BACKEND') 
            if not ret:
                raise Exception(f"Failed to get backend environment: {output}")
            env = output.strip()
            self.getBackedEnvTypeFinished.emit(True, env)
        except Exception as e:
            logging.error(f"Failed to get backend environment: {e}")
            self.getBackedEnvTypeFinished.emit(False, str(e))
            
    # 设置后台信息，demo,dev,pruduction
    def setBackedEnv(self, env: str):
        try:
            ret, output = self._execCommand(f'echo "{env}" > {REMOTE_INSTALL_PATH}/BACKEND && sync') 
            if not ret:
                raise Exception(f"Failed to set backend environment: {output}")
            self.setBackedEnvTypeFinished.emit(True, "Set backend environment successful")
        except Exception as e:
            logging.error(f"Failed to set backend environment: {e}")
            self.setBackedEnvTypeFinished.emit(False, str(e))
    
    def _progress_callback(self, transferred, total):
        percent = (transferred / total) * 100
        logging.debug(f"Transferred: {transferred} / {total} bytes ({percent:.2f}%)")
        
    def _pushFile(self, local_path, remote_path):
        try:
            sftp = self._sshClient.open_sftp()
            sftp.put(local_path, remote_path, callback = self._progress_callback)
            sftp.close()
            logging.debug(f"File {local_path} pushed to {remote_path} successfully")
            return True
        except Exception as e:
            logging.error(f"Failed to push file {local_path} to {remote_path}: {e}")
            return False
        
    def _execCommand(self, command):
        try:
            _, stdout, stderr = self._sshClient.exec_command(command)
            stdout_fd = stdout.channel.fileno()
            stderr_fd = stderr.channel.fileno()
            output = ""
            while True:
                r, _, _ = select.select([stdout_fd, stderr_fd], [], [], 0.1)
                if stdout_fd in r:
                    output += stdout.channel.recv(1024).decode()
                    
                if stderr_fd in r:
                    output += stderr.channel.recv(1024).decode()
                    
                if stdout.channel.exit_status_ready():
                    while stdout.channel.recv_ready():
                        output += stdout.channel.recv(1024).decode()
                        
                    while stderr.channel.recv_stderr_ready():
                        output += stderr.channel.recv_stderr(1024).decode()
                    break
            return True, output
        except Exception as e:
            logging.error(f"Failed to execute command {command}: {e}")
            return False, str(e)
        
    
